﻿// 导航逻辑地图类（初始化部分）
using System;
using System.Collections.Generic;

namespace ShawnNav
{
    public partial class NavMap
    {
        readonly List<int[]> indexArrLst;
        readonly NavVector3[] verticeArr;
        readonly NavPoly[] polyArr;

        // 8_19: 点8 -> 点19 所对应的边
        readonly Dictionary<string, NavBorder> borderDic = new Dictionary<string, NavBorder>();
        // 1_2: 区域1 -> 区域2 共享边
        readonly Dictionary<string, NavBorder> areaIDDic = new Dictionary<string, NavBorder>();

        // 表现映射事件
        public static Action<NavVector3, int> ShowAreaIDViewEvent;
        // 展示路径事件
        public static Action<List<NavPoly>> ShowPathViewEvent;
        // 展示
        public static Action<List<NavVector3>> ShowConnerViewEvent;
        public NavMap(List<int[]> indexArrLst, NavVector3[] verticeArr)
        {
            this.indexArrLst = indexArrLst;
            this.verticeArr = verticeArr;

            // 初始化 区块
            polyArr = new NavPoly[indexArrLst.Count];
            for (int i = 0; i < indexArrLst.Count; i++)
            {
                polyArr[i] = new NavPoly(i, indexArrLst[i], verticeArr);

                // 更新表现
                ShowAreaIDViewEvent?.Invoke(polyArr[i].center, i);
            }

            // 初始化 点 / 区域 与 边界的对应关系
            for (int areaID = 0; areaID < indexArrLst.Count; areaID++)
            {
                int[] indexArr = indexArrLst[areaID]; 
                for (int verticeIndex = 0; verticeIndex < indexArr.Length; verticeIndex++)
                {
                    int startIndex = indexArr[verticeIndex];
                    int endIndex;
                    if (verticeIndex < indexArr.Length - 1)
                    {
                        endIndex = indexArr[verticeIndex + 1];
                    }
                    else
                    {
                        endIndex = indexArr[0];
                    }

                    // 存入 从小到大 的key值
                    string key = string.Empty;
                    if (startIndex < endIndex)
                    {
                        key = $"{startIndex}_{endIndex}";
                    }
                    else
                    {
                        key = $"{endIndex}_{startIndex}";
                    }

                    if (borderDic.TryGetValue(key, out NavBorder border))
                    {
                        border.isShared = true;
                        border.areaID2 = areaID;
                        if (border.areaID1 < border.areaID2)
                        {
                            key = $"{border.areaID1}_{border.areaID2}";
                        }
                        else
                        {
                            key = $"{border.areaID2}_{border.areaID1}";
                        }
                        areaIDDic.Add(key, border);
                    }
                    else
                    {
                        border = new NavBorder
                        {
                            areaID1 = areaID,
                            verticeIndex1 = startIndex,
                            verticeIndex2 = endIndex,
                        };
                        borderDic.Add(key, border);
                    }
                }
            }

            // 移除 非 共享的边界
            List<string> singleLst = new List<string>();
            foreach (var item in borderDic)
            {
                if (!item.Value.isShared)
                {
                    singleLst.Add(item.Key);
                }
            }
            for (int i = 0; i < singleLst.Count; i++)
            {
                borderDic.Remove(singleLst[i]);
            }

            for (int i = 0; i < polyArr.Length; i++)
            {
                polyArr[i].borderLst = GetBorderLstByAreaID(i);
            }
        }

        // 通过 区域ID 获取到 对应区域的共享边界列表
        List<NavBorder> GetBorderLstByAreaID(int areaID)
        {
            List<NavBorder> borderLst = new List<NavBorder>();
            foreach (var item in borderDic)
            {
                if (item.Value.areaID1 == areaID || item.Value.areaID2 == areaID)
                {
                    borderLst.Add(item.Value);
                }
            }
            return borderLst;
        }

        /// <summary>
        /// 寻路主逻辑
        /// </summary>
        /// <param name="startPos"></param>
        /// <param name="targetPos"></param> 
        public List<NavVector3> CalcNavPath(NavVector3 startPos, NavVector3 targetPos)
        {
            int startAreaID = GetAreaIDByPos(startPos);
            int targetAreaID = GetAreaIDByPos(targetPos);
            
            if (startAreaID == -1)
            {
                this.Error($"No area info in startPos:{startPos} ");
                return null;
            }
            // this.Log($"startPos:{startPos}");

            if (targetAreaID == -1)
            {
                this.Error($"No area info in targetPos:{targetPos} ");
                return null;
            }
            // this.Log($"targetPos:{targetPos}");
            NavPoly startArea = polyArr[startAreaID];
            NavPoly targetArea = polyArr[targetAreaID];

            // AStar算法入口
            List<NavPoly> pathAreaLst = CalcPolyPathByAStar(startArea, targetArea);

            // 漏斗拉线算法入口
            List<NavVector3> connerLst = CalcFunnelConnerPath(pathAreaLst, startPos, targetPos);

            ResetAStarData();
            ResetFunnelData();

            ShowConnerViewEvent?.Invoke(connerLst);

            return connerLst;
        }

        // 根据 区域ID 获取到边界信息
        NavBorder GetBorderByAreaID(string key)
        {
            if (areaIDDic.TryGetValue(key, out NavBorder border))
            {
                return border;
            }
            return null;
        }

        // 根据位置信息查找区域ID
        public int GetAreaIDByPos(NavVector3 pos)
        {
            int areaID = -1;
            for (int i = 0; i < polyArr.Length; i++)
            {
                int checkID = polyArr[i].areaID;
                if (IsInsideArea(pos, checkID))
                {
                    areaID = checkID;
                    break;
                }
            }
            return areaID;
        }

        // 判断 目标点 target 是否在 区域areaID 内部
        bool IsInsideArea(NavVector3 target, int areaID) 
        {
            if (areaID >= polyArr.Length)
            {
                return false;
            }
            NavPoly area = polyArr[areaID];
            
            // 包围盒过滤
            if (target.x < area.min.x 
                || target.x > area.max.x 
                || target.z < area.min.z 
                || target.z > area.max.z)
            {
                return false;
            }

            bool rt = false;
            int i, j;
            NavVector3 p0, p1;
            int cnt = area.indexArr.Length;
            for (i = 0, j = cnt - 1; i < cnt; j = i++)
            {
                p0 = verticeArr[area.indexArr[j]];
                p1 = verticeArr[area.indexArr[i]];
                if (IsOnSegnment(p0, p1, target))
                    return true;

                /*
                 PNPoly算法：
                    只要 target.x < 与边焦点 x_val 就说明监测点在交叉点左侧，此时射线必定相交于p1p2连线
                 */
                if ((p0.z < target.z) != (p1.z < target.z)
                    && (target.x < (p1.x - p0.x) * (target.z - p0.z) / (p1.z - p0.z) + p0.x))
                {
                    rt = !rt;
                }
            }
            return rt;
        }

        // 判断 目标点 target 是否在 线段p1p2上（包含p1,p2两点）
        bool IsOnSegnment(NavVector3 p1, NavVector3 p2, NavVector3 target)
        {
            NavVector3 v1 = p1 - target;
            NavVector3 v2 = p2 - target;

            // 满足v1,v2共线的同时，保证target在线段上(投影<=0)
            return NavVector3.CrossXZ(v1, v2) == 0
                && NavVector3.DotXZ(v1, v2) <= 0;
        }
    }
}
